#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include "Sort.h"

using namespace std;

namespace Comun
{
	Sort::Sort(string NArchivo, Atributo COrden, bool ERepetidos, bool Asc){
		NombreArchivo=NArchivo;
		EliminarRepetidos=ERepetidos;
		Ascendente= Asc;
		campo=COrden;
		posCampo=-1;
	}
	Sort::~Sort(){
		}

	string Sort::ObtenerValorDelCampo(string linea){
		char * nombrecampo;
		char aux[1000]="";
		string valor="";
		int i=0;
		strcpy(aux,linea.c_str());
		nombrecampo = strtok(aux,";");
		while (nombrecampo != NULL) {
			if (posCampo==i)
				valor=nombrecampo;
			i++;
			nombrecampo = strtok (NULL, ";");
		}
		
		
		return valor;
	}
	void Sort::CalcularPosicionDelCampo(){         
		string LineaAux;
		char * nombrecampo;
		int i=0;
		ifstream archivo (NombreArchivo.c_str());
  		if (archivo.is_open()){
			getline(archivo,LineaAux);
			archivo.clear();
			archivo.close();
		}
		char aux[1000]="";
		strcpy(aux,LineaAux.c_str());
		nombrecampo = strtok (aux,";");
		while (nombrecampo != NULL) {
			if (nombrecampo==campo.Nombre)
				posCampo=i;
			i++;
			nombrecampo = strtok (NULL, ";");
		}
	}

	void Sort::quicksort( vector<string>* vect, int primero, int ultimo,bool Ascendente ){
		int posicionActual;
 		if (primero >= ultimo ) 
			return;
		posicionActual = particion( vect, primero, ultimo,Ascendente );
		quicksort( vect, primero, posicionActual - 1 ,Ascendente);   
		quicksort( vect, posicionActual + 1, ultimo,Ascendente );    
	}

	void Sort::swap( string * const ptr1, string * const ptr2 )
	{
		string temp;
		temp = *ptr1;
		*ptr1 = *ptr2;
		*ptr2 = temp;
	}
	int Sort::particion( vector<string>* vect, int izquierda, int derecha, bool Ascendente){
		int posicion = izquierda;
		while ( true ) {
			while ( CompararLineas(vect->at(posicion),vect->at(derecha),Ascendente)<=0 && posicion != derecha )
				--derecha;
			if ( posicion == derecha )
			return posicion;
			if ( CompararLineas(vect->at(posicion),vect->at(derecha),Ascendente)>0) {
				swap( &(vect->at(posicion)), &(vect->at(derecha)) );
				posicion = derecha;
			}
			while ( CompararLineas(vect->at(izquierda),vect->at(posicion),Ascendente)<=0 && izquierda != posicion )
				++izquierda;
			if ( posicion == izquierda )
				return posicion;
			if ( CompararLineas(vect->at(izquierda),vect->at(posicion),Ascendente)>0 ) {
				swap( &(vect->at(posicion)), &(vect->at(izquierda)) );
				posicion = izquierda;
			}
		}
	}
	//devuelve  0 si Linea1=Linea2
	//devuelve -1 si Linea1<Linea2
	//devuelve  1 si Linea1>Linea2
	int Sort::CompararLineas(string Linea1, string Linea2, bool Ascendente){
		int valor=0;
		string CampoLinea1=this->ObtenerValorDelCampo(Linea1);
		string CampoLinea2=this->ObtenerValorDelCampo(Linea2);
		string aux="";
		//si orden descendente entonces intercambio las lineas para usar la misma comparacion
		if (!Ascendente){
			aux=CampoLinea1;
			CampoLinea1=CampoLinea2;
			CampoLinea2=aux;
		}
		CondicionMenor* condicion= new CondicionMenor(); 
		if (condicion->esIgual(this->campo.Tipo,CampoLinea1,CampoLinea2))
			valor= 0;
		else
				if (condicion->esMenor(this->campo.Tipo,CampoLinea1,CampoLinea2))
					valor= -1;
					else
						valor= 1;
		return valor;
	}

	string Sort::unirarchivos(int NarchTempMax, bool EliminarRepetidos, bool Ascendente, string Cabecera, string NombreArchivoOrdenado){
		
		string PrefijoArchTemporal="Temp";
		string Linea1="";
		string Linea2="";
		string LineaAux="";
		string UltimaLineaGrabada="";
		int NroArchConLineaMenor=0;
		int PosArchConLineaMenor=0;
		int posAux=0;
		bool leido=false;
		int CantArchLectAbiertos=0;
		vector<bool> nroArchAbierto;//si es true, el archivo temporal con el numero correspondiente a la posicion est� abierto
		int i=0;
		stringstream aux;
		vector<fstream*> archivotemporal;
		ofstream salida (NombreArchivoOrdenado.c_str());
		//grabo la cabecera del archivo
		salida<<Cabecera<<endl;
		//inicializo la lectura de los archivos
		for(i=0;i<NarchTempMax;i++){
			aux<<"Temp"<<i<<".txt";
			archivotemporal.push_back(new fstream);
			archivotemporal.at(i)->open(aux.str().c_str(),fstream::in);
			aux.str("");
			if (archivotemporal.at(i)->is_open()){
				CantArchLectAbiertos++;
				nroArchAbierto.push_back(true);
			}
			else{ 
				nroArchAbierto.push_back(false);
				//ver de informar que un archivo no pudo abrirse
			}
		}
		i=0;
		while (CantArchLectAbiertos!=0){
				//leo la linea del primer archivo que est� abierto
				while(!leido){
					if (nroArchAbierto.at(i)==true){
							PosArchConLineaMenor=archivotemporal.at(i)->tellg();
							NroArchConLineaMenor=i;
							getline(*archivotemporal.at(i),Linea1);
							leido=true;
					}
				i++;
			}
			for(i=i;i<NarchTempMax;i++){
				if (nroArchAbierto.at(i)==true){
					posAux=archivotemporal.at(i)->tellg();
					getline(*archivotemporal.at(i),Linea2);
					if (CompararLineas(Linea1,Linea2,Ascendente)<=0){
						//ya era menor "Linea1" entonces vuelvo atras la posicion del archivo ultimo leido
						archivotemporal.at(i)->seekg(posAux,ios::beg);
						archivotemporal.at(i)->clear();
						}
					else{
						//Intercambio las lineas
						LineaAux=Linea1;
						Linea1=Linea2;
						Linea2=LineaAux;
						//vuelvo atras el que tenia linea menor 1 linea
						archivotemporal.at(NroArchConLineaMenor)->seekg(PosArchConLineaMenor,ios::beg);
						archivotemporal.at(NroArchConLineaMenor)->clear();
						NroArchConLineaMenor=i;
						PosArchConLineaMenor=posAux;
					}
				}
			}
			leido=false;
			i=0;
			//Cierro el archivo que contiene la linea menor si ya lo lei por completo
			if (archivotemporal.at(NroArchConLineaMenor)->eof()){
				archivotemporal.at(NroArchConLineaMenor)->close();
				archivotemporal.at(NroArchConLineaMenor)->clear();
				//en el vector de control indico que ese archivo est� cerrado
				nroArchAbierto.at(NroArchConLineaMenor)=false;
				CantArchLectAbiertos--;
				//borro el archivo
				aux<<"Temp"<<NroArchConLineaMenor<<".txt";
				remove(aux.str().c_str());
				aux.str("");
			}
			archivotemporal.at(NroArchConLineaMenor)->clear();
			if (EliminarRepetidos && Linea1==UltimaLineaGrabada){
				//no gravo al archivo de salida
			} 
			else{
				if (Linea1!=""){
				salida<<Linea1<<endl;
				UltimaLineaGrabada=Linea1;
				}
			}
			Linea1="";
		}
		return NombreArchivoOrdenado;
	}

	long Sort::CalcularBufferLibre(long cantcaracteres,string aux,bool* bufferlleno){
		cantcaracteres=cantcaracteres - strlen(aux.c_str());
		if (cantcaracteres<=0) *bufferlleno=true;
		return cantcaracteres;
	}

	void Sort::SortExterno(string archivoResultado){//char* NombreArchivo, /*Atributo campo,*/ bool EliminarRepetidos, bool Ascendente){
		const long tambuffer=50;
		long cantcaracteres=tambuffer;
		vector<string> lineas;
		string Cabecera="";
		int i=0;
		int j=0;
		int NarchTemp=0;
		int PosArchivo=0;
		bool bufferlleno=false;
		stringstream NombreArchTemp;
		string aux="";
		this->CalcularPosicionDelCampo();
		ifstream archivodesordenado (NombreArchivo.c_str());
  		if (archivodesordenado.is_open()){
			//leo la cabecera del archivo
			getline(archivodesordenado,Cabecera);
   			while (!archivodesordenado.eof()){	
				//una linea cargo si o si
				getline(archivodesordenado,aux);
				lineas.push_back(aux);
				i++;
				//calculos cuantos caracteres libres quedan en el buffer
				cantcaracteres=CalcularBufferLibre(cantcaracteres,aux,&bufferlleno);
				while(!bufferlleno && !archivodesordenado.eof()){
					//leo lineas del archivo
					PosArchivo=archivodesordenado.tellg();
      				getline (archivodesordenado,aux);
					lineas.push_back(aux);
					i++;
					cantcaracteres=CalcularBufferLibre(cantcaracteres,aux,&bufferlleno);
					//Si el buffer se llen� tengo que volver una linea para atras en el archivo
					if (bufferlleno){
						archivodesordenado.seekg(PosArchivo);
						archivodesordenado.clear();
						lineas.pop_back();
						i--;
					}
				}

				//ordeno las lineas que entraron en buffer
				quicksort(&lineas,0,(int)lineas.size()-1,Ascendente);
				//grabo el buffer ordenado en un archivo
				NombreArchTemp<<"Temp"<<NarchTemp<<".txt";
				ofstream salida(NombreArchTemp.str().c_str());
				if (salida.is_open()){
					salida<<lineas.at(0);
					for (j=1; j!=i;j++){
						salida<<endl<<lineas.at(j);
						}
					salida.close();
				}
				else throw "No se pueden crear los archivos temporales";
				NarchTemp++;
				NombreArchTemp.str("");
				lineas.clear(); 
				bufferlleno=false;
				cantcaracteres=tambuffer;
				i=0;
    		}
    		archivodesordenado.close();
			unirarchivos(NarchTemp,EliminarRepetidos, Ascendente, Cabecera, archivoResultado);
  		}
		else cout << "Imposible abrir el archivo"<<endl; 
	}  
}
